package com.chenjia.proto.converter.benchmark;

import com.chenjia.proto.converter.MessageConverter;
import com.chenjia.proto.converter.MethodHandleConverter;
import com.chenjia.proto.converter.demo.Goods;
import com.chenjia.proto.converter.demo.MerchantPojo;
import com.chenjia.proto.converter.demo.message.Merchant;
import com.chenjia.proto.converter.demo.message.RecommendGoods;
import com.vova.rec.protobuf.converter.Converter;
import org.apache.commons.collections4.MapUtils;
import org.openjdk.jmh.annotations.Benchmark;
import org.openjdk.jmh.annotations.Mode;
import org.openjdk.jmh.annotations.Scope;
import org.openjdk.jmh.annotations.State;
import org.openjdk.jmh.runner.Runner;
import org.openjdk.jmh.runner.RunnerException;
import org.openjdk.jmh.runner.options.Options;
import org.openjdk.jmh.runner.options.OptionsBuilder;

import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

@State(Scope.Benchmark)
public class ProtobufConverterBenchmark {

    public static void main(String[] args) throws RunnerException {
        Options options = new OptionsBuilder()
                .include(ProtobufConverterBenchmark.class.getSimpleName())
                .forks(1)
                .mode(Mode.Throughput)
                .warmupIterations(2)
                .measurementIterations(2)
                .build();

        new Runner(options).run();
    }

    private final Converter converter = Converter.create();

    private final RecommendGoods goodsMessage;
    private final Goods goods = Goods.of();

    {
        Merchant merchant = Merchant.newBuilder().setId(233).setName("taobao").setStar(5).build();
        Map<Integer, Merchant> merchantMap = new HashMap<>(4);
        merchantMap.put(1, Merchant.newBuilder().setId(234).setName("taobao4").setStar(2).build());
        merchantMap.put(2, Merchant.newBuilder().setId(235).setName("taobao5").setStar(1).build());
        List<Integer> counts = new ArrayList<>(4);
        counts.add(2);
        counts.add(1);
        counts.add(5);
        counts.add(3);
        goodsMessage = RecommendGoods.newBuilder()
                .setGoodsId(92879182)
                .setBrandId(3928832)
                .setCatId(1.1111F)
                .setFirstCatId(9849832)
                .setGoodsSn("sn3827398")
                .setMerchantId(2333)
                .setShippingFee(false)
                .setShopPrice(2.22323)
                .setMerchant(merchant)
                .putAllMerchants(merchantMap)
                .addAllCounts(counts)
                .build();
    }

    private final Merchant merchantMessage;
    private final MerchantPojo merchantPojo;

    {
        merchantMessage = Merchant.newBuilder().setId(233).setName("chenjia").setStar(5).build();
        merchantPojo = new MerchantPojo(233, "chenjia", 5);
    }


    private final MessageConverter protoConverter = MessageConverter.reflect();
    private final MessageConverter protoConverter2 = MessageConverter.reflectAsm();
    private final MethodHandleConverter protoConverter3 = MessageConverter.methodHandle();

//    @Benchmark
//    public Goods newConverter2ToDomain() {
//        return protoConverter2.toPojo(Goods.class, goodsMessage);
//    }

    //    @Benchmark
    public Goods converter2Domain() {
        return converter.toDomain(Goods.class, goodsMessage);
    }

    //    @Benchmark
    public Goods reflect2Domain() {
        return protoConverter.toPojo(Goods.class, goodsMessage);
    }

    //    @Benchmark
    public Goods native2Domain() {
        Goods goods = new Goods();
        goods.setGoodsId(goodsMessage.getGoodsId());
        goods.setCatId(goodsMessage.getCatId());
        goods.setBrandId(goodsMessage.getBrandId());
        goods.setGoodsSn(goodsMessage.getGoodsSn());
        goods.setShopPrice(goodsMessage.getShopPrice());
        goods.setShippingFee(goodsMessage.getShippingFee());
        goods.setFirstCatId(goodsMessage.getFirstCatId());
        goods.setMerchantId(goodsMessage.getMerchantId());
        goods.setCounts(goodsMessage.getCountsList());
        final Merchant merchant = goodsMessage.getMerchant();
        goods.setMerchant(toMerchantPojo(merchant));
        final Map<Integer, Merchant> merchantsMap = goodsMessage.getMerchantsMap();
        Map<Integer, MerchantPojo> map = new HashMap<>(MapUtils.size(merchantsMap));
        merchantsMap.forEach((k, v) -> map.put(k, toMerchantPojo(v)));
        goods.setMerchants(map);
        return goods;
    }

    private MerchantPojo toMerchantPojo(final Merchant merchant) {
        MerchantPojo pojo = new MerchantPojo();
        pojo.setId(merchant.getId());
        pojo.setName(merchant.getName());
        pojo.setStar(merchant.getStar());
        return pojo;
    }

    // 简单对象

    @Benchmark
    public MerchantPojo native2Pojo() {
        MerchantPojo pojo = new MerchantPojo();
        pojo.setId(merchantMessage.getId());
        pojo.setName(merchantMessage.getName());
        pojo.setStar(merchantMessage.getStar());
        return pojo;
    }

    @Benchmark
    public MerchantPojo converter2Pojo() {
        return converter.toDomain(MerchantPojo.class, merchantMessage);
    }

    @Benchmark
    public MerchantPojo reflect2Pojo() {
        return protoConverter.toPojo(MerchantPojo.class, merchantMessage);
    }

    @Benchmark
    public MerchantPojo reflectAsm2Pojo() {
        return protoConverter2.toPojo(MerchantPojo.class, merchantMessage);
    }

    @Benchmark
    public MerchantPojo converter32Pojo() {
        return protoConverter3.toPojo(MerchantPojo.class, merchantMessage);
    }

    // ---------转 message---------

    //    @Benchmark
    public RecommendGoods native2Protobuf() {
        final RecommendGoods.Builder builder = RecommendGoods.newBuilder().setGoodsId(goods.getGoodsId()).setCatId(goods.getCatId())
                .setBrandId(goods.getBrandId()).setGoodsSn(goods.getGoodsSn())
                .setShopPrice(goods.getShopPrice()).setShippingFee(goods.isShippingFee())
                .setFirstCatId(goods.getFirstCatId()).setMerchantId(goods.getMerchantId());
        builder.setMerchant(toMerchant(goods.getMerchant()));
        builder.addAllCounts(goods.getCounts());
        Map<Integer, Merchant> map = new HashMap<>();
        goods.getMerchants().forEach((k, v) -> map.put(k, toMerchant(v)));
        builder.putAllMerchants(map);
        return builder.build();
    }

    private Merchant toMerchant(MerchantPojo pojo) {
        return Merchant.newBuilder().setId(pojo.getId()).setName(pojo.getName()).setStar(pojo.getStar()).build();
    }

    //    @Benchmark
    public RecommendGoods converter2Protobuf() {
        return converter.toProtobuf(RecommendGoods.class, goods);
    }

    //    @Benchmark
    public RecommendGoods reflect2Protobuf() {
        return protoConverter.toMessage(RecommendGoods.class, goods);
    }

    //    @Benchmark
//    public RecommendGoods newConverter2ToProtobuf() {
//        return protoConverter2.toMessage(RecommendGoods.class, goods);
//    }

    // 简单对象

    //    @Benchmark
    public Merchant native2Message() {
        return Merchant.newBuilder().setId(merchantPojo.getId())
                .setName(merchantPojo.getName())
                .setStar(merchantPojo.getStar()).build();
    }

    //    @Benchmark
    public Merchant converter2Message() {
        return converter.toProtobuf(Merchant.class, merchantPojo);
    }

    //    @Benchmark
    public Merchant reflect2Message() {
        return protoConverter.toMessage(Merchant.class, merchantPojo);
    }

    //    @Benchmark
    public Merchant reflectAsm2Message() {
        return protoConverter2.toMessage(Merchant.class, merchantPojo);
    }
}
/*
// 2020-10-13

 */

/*
Benchmark                                      Mode  Cnt          Score               Error  Units
ProtobufConverterBenchmark.converterToDomain  thrpt    5     185370.768 ±    1878.173       ops/s
ProtobufConverterBenchmark.toDomain           thrpt    5  131059765.436 ± 4200375.168       ops/s

Benchmark                                        Mode  Cnt         Score                Error  Units
ProtobufConverterBenchmark.converterToProtobuf  thrpt    5    154041.853 ±    3167.981          ops/s
ProtobufConverterBenchmark.toProtobuf           thrpt    5  72382302.970 ± 5682030.735          ops/s

// 三者对比：手动、自研库、三方库
Benchmark                                           Mode  Cnt          Score         Error  Units
ProtobufConverterBenchmark.toDomain                thrpt    5  130726232.138 ± 3505657.652  ops/s
ProtobufConverterBenchmark.newConverterToDomain    thrpt    5    6383228.733 ±   69535.177  ops/s
ProtobufConverterBenchmark.converterToDomain       thrpt    5     185002.932 ±    3229.850  ops/s

ProtobufConverterBenchmark.toProtobuf              thrpt    5   73214248.516 ± 5434934.661  ops/s
ProtobufConverterBenchmark.newConverterToProtobuf  thrpt    5    2057858.233 ±   41557.533  ops/s
ProtobufConverterBenchmark.converterToProtobuf     thrpt    5     158171.841 ±    7651.994  ops/s


// 复杂对象测试结果

Benchmark                                       Mode  Cnt         Score        Error  Units
ProtobufConverterBenchmark.reflect2Domain      thrpt    3   1204447.605 ±  72484.987  ops/s
ProtobufConverterBenchmark.converter2Domain    thrpt    3   2452848.376 ± 284493.707  ops/s
ProtobufConverterBenchmark.native2Domain       thrpt    3  12987316.141 ±  52076.714  ops/s

ProtobufConverterBenchmark.reflect2Protobuf    thrpt    3    369201.680 ±   3391.966  ops/s
ProtobufConverterBenchmark.converter2Protobuf  thrpt    3   1600636.763 ±  39088.949  ops/s
ProtobufConverterBenchmark.native2Protobuf     thrpt    3   3858121.248 ±  52353.197  ops/s

// 简单对象测试结果-3个字段
Benchmark                                   Mode  Cnt          Score           Error  Units
ProtobufConverterBenchmark.converter2Pojo  thrpt    3    5519079.751 ±   8626754.026  ops/s
ProtobufConverterBenchmark.native2Pojo     thrpt    3  190373264.348 ± 150135467.621  ops/s
ProtobufConverterBenchmark.reflect2Pojo    thrpt    3    5373706.795 ±   3850761.867  ops/s

Benchmark                                      Mode  Cnt         Score         Error  Units
ProtobufConverterBenchmark.converter2Message  thrpt    3   2131054.642 ±  263446.399  ops/s
ProtobufConverterBenchmark.native2Message     thrpt    3  89944026.195 ± 1392607.179  ops/s
ProtobufConverterBenchmark.reflect2Message    thrpt    3   2070114.041 ±  162953.678  ops/s

// 简单对象增加字段-8个字段
Benchmark                                       Mode  Cnt         Score   Error  Units
ProtobufConverterBenchmark.converter2Message   thrpt    2   1611983.029          ops/s
ProtobufConverterBenchmark.native2Message      thrpt    2  73557688.766          ops/s
ProtobufConverterBenchmark.reflect2Message     thrpt    2   1391386.446          ops/s
ProtobufConverterBenchmark.reflectAsm2Message  thrpt    2   1966174.311          ops/s

Benchmark                                    Mode  Cnt          Score   Error  Units
ProtobufConverterBenchmark.converter2Pojo   thrpt    2    3063132.736          ops/s
ProtobufConverterBenchmark.native2Pojo      thrpt    2  147942231.086          ops/s
ProtobufConverterBenchmark.reflect2Pojo     thrpt    2    3006732.002          ops/s
ProtobufConverterBenchmark.reflectAsm2Pojo  thrpt    2    6247836.167          ops/s
 */
